package cn.moefly.sample.component.annotation;

import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.annotation.*;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@Log4j2
@ComponentScan(basePackages = "cn.moefly.sample.component.annotation",
        excludeFilters = @ComponentScan.Filter(type = FilterType.CUSTOM, classes = ReplaceInDevelopApiPostProcessor.class))
@ConditionalOnProperty(name = ReplaceInDevelopApiPostProcessor.PROPERTY_KEY, havingValue = "true")
@Configuration
public class ReplaceInDevelopApiPostProcessor implements BeanDefinitionRegistryPostProcessor, TypeFilter, Condition {

    static final String PROPERTY_KEY = "debug.rpc.replace-local";

    /**
     * 由于BeanDefinitionRegistryPostProcessor与TypeFilter, Condition不是同一个bean，所以此处使用static保存
     */
    static Map<String, Class> sBeanClassMap = new HashMap<>();

    /**
     * 对Feign接口条件控制注册，以免后来可以用指定的降级类替换
     *
     * @param context
     * @param metadata
     * @return
     */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        String property = context.getEnvironment().getProperty(PROPERTY_KEY);
        return !Boolean.parseBoolean(property);
    }

    /**
     * 使用TypeFilter来获取标记类上的注解信息
     *
     * @param metadataReader
     * @param metadataReaderFactory
     * @return
     */
    @SneakyThrows
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        boolean isFeign = metadataReader.getAnnotationMetadata().hasAnnotation(FeignClient.class.getTypeName());
        boolean isReplace = metadataReader.getAnnotationMetadata().hasAnnotation(ReplaceInDevelop.class.getTypeName());
        if (isFeign && isReplace) {
            Class<?> aClass = Class.forName(metadataReader.getClassMetadata().getClassName());
            String beanName = aClass.getAnnotation(FeignClient.class).name();
            Class<?> replaceClass = aClass.getAnnotation(ReplaceInDevelop.class).value();
            sBeanClassMap.put(beanName, replaceClass);
        }
        return false;
    }

    /**
     * 注入替代的Bean
     *
     * @param registry
     * @throws BeansException
     */
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        var map = sBeanClassMap;
        sBeanClassMap = null;
        map.forEach((k, v) -> registry.registerBeanDefinition(k, new RootBeanDefinition(v)));
    }


    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    }

}
